// Copyright (c) 2025 Inochi Amaoto

/* SPDX-License-Identifier: GPL-2.0 */

#ifndef __VHOST_VAPP_DEV_H__
#define __VHOST_VAPP_DEV_H__

#include "vhost-vapp.h"
#include "vhost-vapp-dtq.h"
#include "vio-entity.h"

static void vhost_vapp_dtq_stop(struct vhost_vapp_dtq* dtq)
{
  for (size_t i = 0; i < ARRAY_SIZE(dtq->vqs); i++) {
    struct vhost_virtqueue* vq = &dtq->vqs[i];
    mutex_lock(&vq->mutex);
    vhost_vq_set_backend(vq, NULL);
    mutex_unlock(&vq->mutex);
  }
}

static int vhost_vapp_dtq_start(struct vhost_vapp_dtq* dtq)
{
  int ret;
  struct vhost_virtqueue *vq;
  for (size_t i = 0; i < ARRAY_SIZE(dtq->vqs); i++) {
    vq = &dtq->vqs[i];

    mutex_lock(&vq->mutex);

    if (!vhost_vq_access_ok(vq)) {
      ret = -EFAULT;
      goto vq_failed;
    }

    if (!vhost_vq_get_backend(vq)) {
      vhost_vq_set_backend(vq, dtq);
      ret = vhost_vq_init_access(vq);
      if (ret)
        goto vq_failed;
    }

    mutex_unlock(&vq->mutex);
  }

  return 0;

 vq_failed:
  vhost_vq_set_backend(vq, NULL);
  mutex_unlock(&vq->mutex);
  vhost_vapp_dtq_stop(dtq);
  return ret;
}

static void vhost_vapp_dev_init(struct vhost_vapp_dev* dev)
{
  dev->conf.uuid = 0;
  dev->conf.multiqueues = 0;
  dev->ddtqs = NULL;

  vapp_send_queue_init(&dev->cqueue);
  vapp_send_queue_init(&dev->dqueue);

  vhost_vapp_dtq_init_ctrl(&dev->cdtq);
  vhost_vapp_dtq_set_backend(&dev->cdtq, dev);
  vhost_vapp_dtq_set_queue(&dev->cdtq, &dev->cqueue);

  vapp_entity_tree_init(&dev->req);
  vapp_entity_tree_init(&dev->rsp);
}

static struct vhost_vapp_dev* vhost_vapp_dev_alloc(void)
{
  struct vhost_vapp_dev* dev = vmalloc(sizeof(struct vhost_vapp_dev));
  if (!dev)
    return NULL;

  vhost_vapp_dev_init(dev);
  return dev;
}

static void vhost_vapp_dev_destroy(struct vhost_vapp_dev* dev)
{
  vapp_send_queue_destory(&dev->cqueue, struct vapp_send_ctrl_data_packet, node, vapp_send_ctrl_data_packet_free);
  vapp_send_queue_destory(&dev->dqueue, struct vapp_send_data_packet, node, vapp_send_data_packet_free);
}

/**
 * TODO: only free memory, must free all request befor this is called.
 */
static void vhost_vapp_dev_free(struct vhost_vapp_dev* dev)
{
  if (likely(dev->ddtqs))
    kvfree(dev->ddtqs);
  kvfree(dev);
}

#endif // ! __VHOST_VAPP_DEV_H__
